//
// objmodel.c
// much of this is based on the work of ian piumarta
// <http://www.piumarta.com/pepsi/objmodel.pdf>
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "potion.h"
#include "internal.h"
#include "khash.h"
#include "table.h"
#include "asm.h"

// 创建函数 meth 的闭包： .data[extra] = {PN_NIL} ，sig 签名为非空元组则设置。
// TODO hsq 了解与之相关的各个函数的用处
PN potion_closure_new(Potion *P, PN_F meth, PN sig, PN_SIZE extra) {
  PN_SIZE i;
  vPN(Closure) c = PN_ALLOC_N(PN_TCLOSURE, struct PNClosure, extra * sizeof(PN));
  c->method = meth;
  if (PN_IS_TUPLE(sig) && PN_TUPLE_LEN(sig) > 0)
    c->sig = sig;
  c->extra = extra;
  for (i = 0; i < c->extra; i++)
    c->data[i] = PN_NIL;
  return (PN)c;
}

// 取闭包 self 的函数原型 .data[0] ，无则为 nil 。
PN_MSG(potion_closure_code) {
  if (PN_CLOSURE(self)->extra > 0 && PN_IS_PROTO(PN_CLOSURE(self)->data[0]))
    return PN_CLOSURE(self)->data[0];
  return PN_NIL;
}

// 返回闭包 self 的签名 .sig 字符串。
// TODO hsq 缓存？这是 PNClosure 的 string 消息。
PN_MSG(potion_closure_string) {
  int x = 0;
  PN out = potion_bytes_str(P, "function(");
  if (PN_IS_TUPLE(PN_CLOSURE(self)->sig)) {
    PN_TUPLE_EACH(PN_CLOSURE(self)->sig, i, v, {
      if (PN_IS_STR(v)) {
        if (x++ > 0) POTION_BYTES_LITERAL(out, ", ");
        potion_bytes_obj_string(P, out, v);
      }
    });
  }
  POTION_BYTES_LITERAL(out, ")");
  return PN_STR_B(out);
}

// 创建空的虚表 struct PNVtable ，注册到 VM.vts 并返回。
//    .type=t, .parent=parent, .methods=空表 。
PN potion_type_new(Potion *P, PNType t, PN parent) {
  vPN(Vtable) vt = PN_CALLOC(PN_TVTABLE, struct PNVtable);
  vt->type    = t;
  vt->parent  = parent;
  vt->methods = (struct PNTable *)potion_table_empty(P);
  vt->name    = PN_NIL;
  PN_VTABLE(t) = (PN)vt;
  return (PN)vt;
}

// 设置虚表的 .call 闭包。
void potion_type_call_is(PN vt, PN cl) {
  ((struct PNVtable *)vt)->call = cl;
}

// 返回对象的 .call 闭包，缺省为 VM 的通用 .call 闭包。
PN potion_obj_get_call(Potion *P, PN obj) {
  PN cl = ((struct PNVtable *)PN_VTABLE(PN_TYPE(obj)))->call;
  if (cl == PN_NIL) cl = P->call;
  return cl;
}

// 设置虚表的 .callset 闭包。
void potion_type_callset_is(PN vt, PN cl) {
  ((struct PNVtable *)vt)->callset = cl;
}

// 返回对象的 .callset 闭包，缺省为 VM 的通用 .callset 闭包。
PN potion_obj_get_callset(Potion *P, PN obj) {
  PN cl = ((struct PNVtable *)PN_VTABLE(PN_TYPE(obj)))->callset;
  if (cl == PN_NIL) cl = P->callset;
  return cl;
}

// 返回消息接收者自身。
PN_MSG(potion_no_call) {
  return self;
}

static PN potion_ivars(Potion *P, PN vtable, PN ivars);

// 创建类（即虚表，见 potion_type_new ，类型 ID 递增）注册到 VM.vts 并返回：
//  parent  是 Lobby 则以 object 作为父类，否则用 parent ；
//  closure 是构造函数，可 nil ，无则取父类的；
//  ivars   是属性元组，可 nil ，父类的如非空则直接拷贝进来；
PN potion_class(Potion *P, PN closure, PN parent, PN ivars) {
  if (parent == P->lobby) parent = PN_VTABLE(PN_TOBJECT);
  // TODO hsq parent 是否可能为 nil ？
  PN pvars = ((struct PNVtable *)parent)->ivars;
  PNType t = PN_FLEX_SIZE(P->vts) + PN_TNIL;
  PN_FLEX_NEEDS(1, P->vts, PN_TFLEX, PNFlex, TYPE_BATCH_SIZE);
  PN sub = potion_type_new(P, t, parent);
  if (PN_IS_TUPLE(pvars)) {
    if (!PN_IS_TUPLE(ivars)) ivars = PN_TUP0();
    PN_TUPLE_EACH(pvars, i, v, {PN_PUT(ivars, v);});
  }
  if (PN_IS_TUPLE(ivars))
    potion_ivars(P, sub, ivars);

  if (!PN_IS_CLOSURE(closure))
    closure = ((struct PNVtable *)parent)->ctor;
  ((struct PNVtable *)sub)->ctor = closure;

  PN_FLEX_SIZE(P->vts)++;
  PN_TOUCH(P->vts);
  return sub;
}

// 设置虚表 self 的属性元组字段 .ivars 和 .ivlen 。
static PN potion_ivars(Potion *P, PN vtable, PN ivars) {
  struct PNVtable *vt = (struct PNVtable *)vtable;
  #if POTION_JIT == 1
    // TODO: allocate assembled instructions together into single pages
    // since many times these tables are <100 bytes.
    if (P->jit_on) {
      PNAsm * volatile asmb = potion_asm_new(P);
      P->target.ivars(P, ivars, &asmb);
      vt->ivfunc = PN_ALLOC_FUNC(asmb->len);
      PN_MEMCPY_N(vt->ivfunc, asmb->ptr, u8, asmb->len);
      PN_EXEC_FUNC(vt->ivfunc, asmb->len);
    }
  #endif
  vt->ivlen = PN_TUPLE_LEN(ivars);
  vt->ivars = ivars;
  return vtable;
}

// 在 self 的属性元组 .ivar 中查找属性字串 ivar ，返回其索引 | -1 ；系统内建虚表 -1 。
static inline long potion_obj_find_ivar(Potion *P, PN self, PN ivar) {
  PNType t = PN_TYPE(self);
  vPN(Vtable) vt = (struct PNVtable *)PN_VTABLE(t);
  #if POTION_JIT == 1
    if (P->jit_on && vt->ivfunc != NULL)
      return vt->ivfunc(PN_UNIQ(ivar));
  #endif

  if (t > PN_TBUILTIN_END) {
    PN ivars = vt->ivars;
    if (ivars != PN_NIL) {
      // TODO hsq ivars 并没基于各项的哈希值升序排列
      // return potion_tuple_binary_search(ivars, ivar);
      PN_TUPLE_EACH(ivars, i, v, {
        if (ivar == v) return i;
      });
    }
  }
  return -1;
}

// 取对象 obj 的属性 /<ivar> 值，若不存在则为 nil 。
PN potion_obj_get(Potion *P, PN obj, PN ivar) {
  long i = potion_obj_find_ivar(P, obj, ivar);
  if (i >= 0)
    return ((struct PNObject *)obj)->ivars[i];
  return PN_NIL;
}

// 设置对象 obj 的属性 /<ivar> 值为 value ，不存在则无操作，并返回 value 。
PN potion_obj_set(Potion *P, PN obj, PN ivar, PN value) {
  long i = potion_obj_find_ivar(P, obj, ivar);
  if (i >= 0) {
    ((struct PNObject *)obj)->ivars[i] = value;
    PN_TOUCH(obj);
  }
  return value;
}

// 用于字节码模式的闭包的 .method 字段，调用闭包时：
//  运行虚表 Lobby 中的闭包 .data[0] 处的 proto 字节码，无 upval ，并传入参数元组 args 。
//  类似 potion_vm_proto 。
PN_MSG(potion_proto_method, PN args) {
  return potion_vm(P, PN_CLOSURE(closure)->data[0], P->lobby, args, 0, NULL);
}

// 用于字节码模式的闭包的 .method 字段，调用闭包时：
//  取其函数原型（ .data[0] ）。
PN_MSG(potion_getter_method) {
  return PN_CLOSURE(closure)->data[0];
}

// 设置并返回闭包： self(vtable)[key] = method ； method 可为闭包，
//  否则创建闭包且其 .data[0]=method ，此时若 ：
//    method 为函数原型，则调用闭包时在根命名空间下执行原型并传入参数；
//    否则该闭包被调用时将返回 .data[0] 即 method 。
PN_MSG_G(potion_def_method, PN key, PN method) {
  int ret;
  PN cl;
  vPN(Vtable) vt = (struct PNVtable *)self;
  unsigned k = kh_put(PN, vt->methods, key, &ret);
  PN_QUICK_FWD(struct PNTable *, vt->methods);
  PN_TOUCH(vt->methods);

  if (!PN_IS_CLOSURE(method)) {
    if (PN_IS_PROTO(method))
      cl = potion_closure_new(P, (PN_F)potion_proto_method, PN_PROTO(method)->sig, 1);
    else
      cl = potion_closure_new(P, (PN_F)potion_getter_method, PN_NIL, 1);
    PN_CLOSURE(cl)->data[0] = method;
    method = cl;
  }

  kh_val(PN, vt->methods, k) = method;
  PN_TOUCH(self);

  #if POTION_JIT == 1 && defined(JIT_MCACHE)
    // TODO: make this more flexible, store in fixed gc, see ivfunc TODO also
    // this is disabled until method weakrefs can be stored in fixed memory
    if (P->jit_on && P->target.mcache != NULL) {
      PNAsm * volatile asmb = potion_asm_new(P);
      P->target.mcache(P, vt, &asmb);
      if (asmb->len <= 4096) {
        if (vt->mcache == NULL)
          vt->mcache = PN_ALLOC_FUNC(4096);
        else
          PN_WRITE_FUNC(vt->mcache, 4096);
        PN_MEMCPY_N(vt->mcache, asmb->ptr, u8, asmb->len);
        PN_EXEC_FUNC(vt->mcache, 4096);
      } else if (vt->mcache != NULL) {
        potion_munmap(vt->mcache, 4096);
        vt->mcache = NULL;
      }
    }
  #endif
  return method;
}

// 在 self(vtable) 中查找 key 并返回，可能返回 PN_NIL ；未用 closure 。
PN_MSG_G(potion_lookup, PN key) {
  vPN(Vtable) vt = (struct PNVtable *)self;
  #if POTION_JIT == 1 && defined(JIT_MCACHE)
    if (P->jit_on && vt->mcache != NULL)
      return vt->mcache(PN_UNIQ(key));
  #endif
  unsigned k = kh_get(PN, vt->methods, key);
  if (k != kh_end(vt->methods)) return kh_val(PN, vt->methods, k);
  return PN_NIL;
}

// 在 rcv 的虚表中查找 msg 对应的对象，沿着虚表的继承链。
// TODO hsq 实现上与原版 objmodel 略有不同。
PN potion_bind(Potion *P, PN rcv, PN msg) {
  PN closure = PN_NIL;
  PN vt = PN_NIL;
  PNType t = PN_TYPE(rcv);
  if (!PN_TYPECHECK(t)) return PN_NIL;
  vt = PN_VTABLE(t);
  while (PN_IS_PTR(vt)) {
    closure = ((msg == $(lookup)) && (t == PN_TVTABLE))
      ? potion_lookup(P, PN_NIL, vt, msg)
      : potion_send(vt, $(lookup), msg);
    if (closure) break;
    vt = ((struct PNVtable *)vt)->parent;
  }
  return closure;
}

// 同 potion_bind ，区别是
//    如得到的对象是闭包且其参数签名为 nil 则调用并返回结果。
PN potion_message(Potion *P, PN rcv, PN msg) {
  PN cl = potion_bind(P, rcv, msg);
  if (PN_IS_CLOSURE(cl) && PN_CLOSURE(cl)->sig == PN_NIL)
    return PN_CLOSURE(cl)->method(P, cl, rcv, PN_NIL);
  return cl;
}

// 向 a 发消息： + (b)
PN potion_obj_add(Potion *P, PN a, PN b) {
  return potion_send(a, $(add), b);
}

// 向 a 发消息： - (b)
PN potion_obj_sub(Potion *P, PN a, PN b) {
  return potion_send(a, $(sub), b);
}

// 向 a 发消息： * (b)
PN potion_obj_mult(Potion *P, PN a, PN b) {
  return potion_send(a, $(mult), b);
}

// 向 a 发消息： / (b)
PN potion_obj_div(Potion *P, PN a, PN b) {
  return potion_send(a, $(div), b);
}

// 向 a 发消息： % (b)
PN potion_obj_rem(Potion *P, PN a, PN b) {
  return potion_send(a, $(rem), b);
}

// 向 a 发消息： ~
PN potion_obj_bitn(Potion *P, PN a) {
  return potion_send(a, $(bitn));
}

// 向 a 发消息： << (b)
PN potion_obj_bitl(Potion *P, PN a, PN b) {
  return potion_send(a, $(bitl), b);
}

// 向 a 发消息： >> (b)
PN potion_obj_bitr(Potion *P, PN a, PN b) {
  return potion_send(a, $(bitr), b);
}

// 向 a 发消息： | (b)
PN potion_obj_pipe(Potion *P, PN a, PN b) {
  return potion_send(a, $(pipe), b);
}

// 向 a 发消息： ^ (b)
PN potion_obj_caret(Potion *P, PN a, PN b) {
  return potion_send(a, $(caret), b);
}

// 向 a 发消息： & (b)
PN potion_obj_amp(Potion *P, PN a, PN b) {
  return potion_send(a, $(amp), b);
}

// 如果 data 是 ref 则直接返回，否则创建 PNWeakRef 包装 data 。
PN potion_ref(Potion *P, PN data) {
  if (PN_IS_REF(data)) return data;
  vPN(WeakRef) ref = PN_ALLOC(PN_TWEAK, struct PNWeakRef);
  ref->data = data;
  return (PN)ref;
}

// 弱引用类型的 string 消息闭包。
// TODO hsq 全局引用避免多次调用？以及下面的；以及 potion_continuation_string 。
//    TODO hsq 或者像 nil 的 string 消息那样，直接绑定到字符串而非闭包？
PN_MSG(potion_ref_string) {
  return PN_LITERAL("<ref>");
}

// object 类型的 string 消息闭包。
PN_MSG(potion_object_string, PN len) {
  return PN_LITERAL("<object>");
}

// object 类型的 forward 消息闭包：打印而非返回，返回 nil 。
// TODO hsq 有何用途？原有参数 PN method 作何用途？
PN_MSG(potion_object_forward) {
  printf("#<object>");
  return PN_NIL;
}

// object 类型的 send 消息闭包。
PN_MSG(potion_object_send, PN method) {
  return potion_send(self, method);
}

// 创建 PNObject 对象，其 .ivars 为虚表 self.ivlen 个 PN 。
PN_MSG_G(potion_object_new) {
  vPN(Vtable) vt = (struct PNVtable *)self;
  return (PN)PN_ALLOC_N(vt->type, struct PNObject, vt->ivlen * sizeof(PN));
}

// 返回消息接收者自身。
PN_MSG(potion_lobby_self) {
  return self;
}

// 获取 self 的虚表，否则 nil 。
PN_MSG(potion_lobby_kind) {
  PNType t = PN_TYPE(self);
  if (!PN_TYPECHECK(t)) return PN_NIL; // TODO: error
  return PN_VTABLE(t);
}

// 列举 vtable 注册的消息，返回其 tuple 。
PN_MSG(potion_kind_messages) {
  vPN(Vtable) vt = (struct PNVtable *)self;
  PN ss = PN_TUP0();
  KH_EACH_ASC(PN, vt->methods, {
    PN_PUSH(ss, kh_key(PN, t, k));
  });
  return ss;
}

// 字符串化 vtable 。
PN_MSG(potion_kind_string) {
  // return PN_NUM(PN_TYPE_ID(PN_TYPE(self)));
  vPN(Vtable) vt = (struct PNVtable *)self;
  // return PN_NUM(vt->type);
  return (vt->name != PN_NIL)
    ? vt->name
    : potion_str_format(P, "Kind#%d", PN_TYPE_ID(vt->type));

  /* TODO hsq 为何找不到？
  vPN(Vtable) lobby = (struct PNVtable *)P->lobby;
  printf("%lx\t\t%d\n", PN_VTABLE(vt->type), vt->type - PN_TNIL);
  printf("%lx\n", self);
  KH_EACH_ASC(PN, lobby->methods, {
    PN val = kh_val(PN, t, k);
    printf("%lx\t\t", val);
    PN key = kh_key(PN, t, k);
    STR_AND_PRINT(key);
    STR_AND_PRINT(PN_LITERAL("\n"));
    if (self == val) return key;
  });
  return PN_LITERAL("** Error **");
  //*/
}

// 给 vtable 命名，返回 true ｜ Error 。
PN_MSG(potion_kind_name, PN name) {
  vPN(Vtable) vt = (struct PNVtable *)self;
  if (!PN_IS_STR(name) || PN_STR_LEN(name) < 1) {
    return ERROR("invalid name: <%s>", PN_STR_PTR(potion_send(name, $(string))));
  }
  // TODO hsq 名字的唯一性？自动设置？
  if (vt->name != PN_NIL) {
    return ERROR("Already have a name: <%s>", PN_STR_PTR(potion_send(self, $(string))));
  }
  vt->name = name;
  return PN_TRUE;
}

// 列举注册的 vtable ，返回 tuple 。
PN_MSG(potion_kind_list) {
  PN ms = PN_TUP0();
  PN_SIZE len = PN_FLEX_SIZE(P->vts);
  for (PN_SIZE t = 0; t < len; t++) {
    PN_PUSH(ms, PN_VTABLE(t + PN_TNIL));
  }
  return ms;
}

// 创建一个“言论”表。
// TODO hsq 启动时构造好，而不是每次调用都构造？而且这些字符串没必要捕获？
PN_MSG(potion_about) {
  PN about = potion_table_empty(P);
  potion_table_put(P, PN_NIL, about, PN_LITERAL("_why"),
    PN_LITERAL("“I love _why, but learning Ruby from him is like trying to learn to pole vault "
      "by having Salvador Dali punch you in the face.” - Steven Frank"));
  potion_table_put(P, PN_NIL, about, PN_LITERAL("minimalism"),
    PN_LITERAL("“The sad thing about ‘minimalism’ is that it has a name.” "
      "- Steve Dekorte"));
  potion_table_put(P, PN_NIL, about, PN_LITERAL("stage fright"),
    PN_LITERAL("“Recently no move on Potion. I git pull everyday.” "
      "- matz"));
  potion_table_put(P, PN_NIL, about, PN_LITERAL("terms of use"),
    PN_LITERAL("“Setting up my new anarchist bulletin board so that during registration, if you accept "
      "the terms and conditions, you are banned forever.” - Dr. Casey Hall"));
  potion_table_put(P, PN_NIL, about, PN_LITERAL("help"),
    PN_LITERAL("`man which` - Evan Weaver"));
  potion_table_put(P, PN_NIL, about, PN_LITERAL("ts"),
    PN_LITERAL("“pigeon%” - Guy Decoux (1955 - 2008)"));
  potion_table_put(P, PN_NIL, about, PN_LITERAL("summary"),
    PN_LITERAL("“I smell as how a leprechaun looks.” - Alana Post"));
  return about;
}

// 销毁 VM ，正常退出程序。
PN_MSG(potion_exit) {
  potion_destroy(P);
  exit(EXIT_SUCCESS);
  return PN_NUM(EXIT_SUCCESS);
}

#if POTION_JIT == 1
// 是否支持 JIT
PN_MSG(potion_jit_has) {
  return PN_BOOL(P->jit_has);
}

// 设置是否开启 JIT ；不支持则不可更改， on=nil 则不设置；返回旧值。
PN_MSG_G(potion_jit_on, PN on) {
  PN old;
  if (!P->jit_has) return PN_FALSE;
  old = PN_BOOL(P->jit_on);
  if (on != PN_NIL) P->jit_on = PN_TEST(on);
  return old;
}
#endif

// 设置是否在 REPL 中；返回旧值。
PN_MSG(potion_in_repl, PN in) {
  PN old = PN_BOOL(P->in_repl);
  if (in != PN_NIL) P->in_repl = PN_TEST(in);
  return old;
}

// 缺省 print 消息，先字符串化再 print ，返回 nil 。
// PN_MSG(potion_object_print) {
//   STR_AND_PRINT(self);
//   return PN_NIL;
// }

// 注册虚表 closure ref(weak) object 的消息。
void potion_object_init(Potion *P) {
  PN clo_vt = PN_VTABLE(PN_TCLOSURE);
  PN ref_vt = PN_VTABLE(PN_TWEAK);
  PN obj_vt = PN_VTABLE(PN_TOBJECT);
  PN_METHOD(clo_vt, "code",    potion_closure_code,   0);
  PN_METHOD(clo_vt, "string",  potion_closure_string, 0);
  PN_METHOD(ref_vt, "string",  potion_ref_string,     0);
  PN_METHOD(obj_vt, "forward", potion_object_forward, 0);
  PN_METHOD(obj_vt, "send",    potion_object_send,    "method=o");
  PN_METHOD(obj_vt, "string",  potion_object_string,  0);
  // PN_METHOD(obj_vt, "print",   potion_object_print,   0);
}

// 在根命名空间 Lobby 中注册各个虚表对应的消息，及其他若干消息；
// TODO hsq 这些虚表消息如何使用？像 about 等消息那样无接收者？
// 设置 VM 的通用 .call .callset 为返回接收者自身的闭包；
// 设置虚表 vtable 的 .call 为创建 PNObject 对象的闭包；
void potion_lobby_init(Potion *P) {
  // PN_DEF(P->lobby, "Lobby",        P->lobby);
  // PN_DEF(P->lobby, "Mixin",        PN_VTABLE(PN_TVTABLE));
  // ...

  // PN_DEF 不行，所以用 potion_send
  #define PN_KIND(n, t) \
    potion_send(P->lobby, $(def), PN_LITERAL(n), PN_VTABLE(t)), \
    ((struct PNVtable *)PN_VTABLE(t))->name = PN_LITERAL(n)
  PN_KIND("Lobby",        PN_TLOBBY);
  PN_KIND("Mixin",        PN_TVTABLE);
  PN_KIND("Object",       PN_TOBJECT);
  PN_KIND("NilKind",      PN_TNIL);
  PN_KIND("Number",       PN_TNUMBER);
  PN_KIND("Boolean",      PN_TBOOLEAN);
  PN_KIND("String",       PN_TSTRING);
  PN_KIND("Table",        PN_TTABLE);
  PN_KIND("Function",     PN_TCLOSURE);
  PN_KIND("Tuple",        PN_TTUPLE);
  PN_KIND("Potion",       PN_TSTATE);
  PN_KIND("Source",       PN_TSOURCE);
  PN_KIND("Bytes",        PN_TBYTES);
  PN_KIND("Compiled",     PN_TPROTO);
  PN_KIND("Ref",          PN_TWEAK);
  PN_KIND("Lick",         PN_TLICK);
  PN_KIND("Error",        PN_TERROR);
  PN_KIND("Continuation", PN_TCONT);
  #undef PN_KIND

  // PN_DEF(P->lobby, "string",       PN_LITERAL("Lobby"));
  // PN_METHOD(P->lobby, "string",  potion_kind_string, 0);
  PN_METHOD(PN_VTABLE(PN_TVTABLE), "string",   potion_kind_string,   0);
  PN_METHOD(PN_VTABLE(PN_TVTABLE), "name",     potion_kind_name,     "name=S");
  PN_METHOD(PN_VTABLE(PN_TVTABLE), "messages", potion_kind_messages, 0);

  P->call = P->callset = PN_FUNC(potion_no_call, 0);
  potion_type_call_is(PN_VTABLE(PN_TVTABLE), PN_FUNC(potion_object_new, 0));
  PN_METHOD(P->lobby, "about",   potion_about,      0);
  PN_METHOD(P->lobby, "exit",    potion_exit,       0);

  PN_METHOD(P->lobby, "kind",    potion_lobby_kind, 0);
  PN_METHOD(P->lobby, "kinds",   potion_kind_list,  0);
  PN_METHOD(P->lobby, "self",    potion_lobby_self, 0);

  #if POTION_JIT == 1
  PN_METHOD(P->lobby, "jit_has", potion_jit_has,    0);
  PN_METHOD(P->lobby, "jit_on",  potion_jit_on,     "on=b");
  #endif
  PN_METHOD(P->lobby, "in_repl", potion_in_repl,    "in=b");
}
